#include <fstream>
#include <iostream>
#include <string>
#include <vector>
using namespace std;

#include "nethelp.h"

size_t find_arg(vector<string>& varg, const char* name)
{
    for(size_t i = 0; i < varg.size(); ++i)
    {
        if( !_stricmp(varg[i].c_str(), name) )
        {
            return i;
        }
    }
    return varg.size();
}

string& get_arg(vector<string>& varg, const char* name)
{
    size_t argc = varg.size() - 1;
    for(size_t i = 0; i < argc; ++i)
    {
        if( !_stricmp(varg[i].c_str(), name) )
        {
            return varg[i+1];
        }
    }
    return varg.back();
}

void trim(string& s, const string& sp)
{
    while( !s.empty() && sp.find(s.back()) != string::npos )
    {
        s.pop_back();
    }

    if( !s.empty() && sp.find(s.front()) != string::npos )
    {
        string::iterator end = s.begin() + 1;
        while( end != s.end() && sp.find(*end) != string::npos )
        {
            ++end;
        }
        s.erase(s.begin(), end);
    }
}

bool find_adapter(const string& hint, adapter_inf& inf)
{
    vector<adapter_inf> adl;
    if( hint.empty() || !get_adapter_list(adl) )
    {
        return false;
    }

    bool hint_is_ip = is_ipv4_address(hint);

    for(size_t i = 0; i < adl.size(); ++i)
    {
        if( (hint_is_ip && adl[i].unicast == hint) ||
            (!hint_is_ip && !strcmp(adl[i].name.c_str(), hint.c_str())) )
        {
            inf = adl[i];
            return true;
        }
    }

    return false;
}

void usage()
{
    static const char copyleft[] =
        "mkroute 0.1b Licensed under WTFPL without warranty\n";

    cout<< copyleft << '\n'
        << "Usage: mkroute [-d -q -t] [-m <METRIC>] -i <INTF> -f <FILE>\n\n"
        << "    -d          delete all routes destined for networks listed in FILE\n"
        << "    -q          be quiet, no output if everything is OK. Ignored in test mode\n"
        << "    -t          test mode, do not change system route table, output \n"
        << "                equivalent system commands to standard output\n"
        << "    -m METRIC   route metric, optional\n"
        << "    -i INTF     interface hint, can be either IPv4 address or adapter name\n"
        << "                optional if -d is specified, but some performance will lost\n"
        << "    -f FILE     CIDR address(format 10.10.10.0/24) list.\n"
        << "  Order of switches doesn't matter.\n\n"
        << "Examples:\n"
        << "    mkroute -i 192.168.1.1 -f CN_CIDR.txt\n"
        << "    mkroute -i LAN -m 10 -f CN_CIDR.txt\n"
        << "    mkroute -d -f CN_CIDR.txt -i LAN\n\n";
}

int main(int argc, char* argv[])
{
    sock_startup();
    if( argc == 2 && ( !_stricmp(argv[1], "-h") || !_stricmp(argv[1], "--help")) )
    {
        usage();
        return 0;
    }

    if( argc < 4 || argc > 10 )
    {
        cerr << "Error: invalid usage, type \'mkroute -h\' for help.\n";
        return -1;
    }

    vector<string> varg;
    for(int i = 0; i < argc; ++i)
    {
        varg.push_back(argv[i]);
    }
    varg.push_back("");

    bool quiet = find_arg(varg, "-q") != varg.size();

    bool test_only = find_arg(varg, "-t") != varg.size();

    // Activate route or deactivate route
    bool enable = find_arg(varg, "-d") == varg.size();

    // Set default metric to -1
    size_t  metric = atoi(get_arg(varg, "-m").c_str());
    if( metric == 0 || metric > 65535 )
    {
        metric = (size_t)-1;
    }

    // Gateway interface IP address or hint
    string  ifhint = get_arg(varg, "-i");
    if( ifhint.empty() && enable )
    {
        cerr << "Error: missing interface, use -i to specify a interface hint.\n";
        return -1;
    }

    adapter_inf intf;
    if( !find_adapter(ifhint, intf) && enable )
    {
        cerr << "Error: can't find interface by hint " << ifhint << '\n';
        return -1;
    }

    string  file_name = get_arg(varg, "-f");
    if( file_name.empty() )
    {
        cerr << "Error: missing argument for -f.\n";
        return -1;
    }

    ifstream address_list(file_name.c_str());
    if( !address_list.is_open() || address_list.fail() )
    {
        cerr << "Error: failed to open file " << file_name << '\n';
        return -1;
    }

    string whitespace(" \t\r\n");
    string address;

    if( test_only )
    {
        cout<< "rem File:      " << file_name << '\n'
            << "rem IF-IPv4:   " << intf.unicast << '\n'
            << "rem IF-LUID:   " << std::hex << intf.luid.Value << std::dec << '\n'
            << "rem Gateway:   " << intf.gateway << '\n'
            << "rem Metric:    " << get_arg(varg, "-m") << '\n';
    }

    size_t success = 0;
    while( !address_list.eof() )
    {
        getline(address_list, address);
        trim(address, whitespace);

        // Filtering blank line and comments
        if( address.empty() || address.front() == '#' )
        {
            continue;
        }

        // Examine address, minimal address 0.0.0.0/0
        if( address.length() < 9 || !isdigit(address.front()) )
        {
            cerr << "Error: unknown content ignored: " << address << '\n';
            continue;
        }

        int masks = 32;
        string::size_type p;
        if( (p=address.find_first_of('/')) != string::npos )
        {
            masks = atoi(address.c_str()+p+1);
        }
        address.erase(p);

        if( test_only )
        {
            if( enable )
            {
                cout<< "ROUTE ADD " << address << '/' << masks << ' ' << intf.gateway;
                if( metric != -1 )
                {
                    cout << " METRIC " << metric;
                }
                cout<< '\n';
            }
            else
            {
                cout<< "ROUTE DELETE " << address << '/' << masks << '\n';
            }
        }
        else if( !make_route(enable, (char*)address.c_str(), masks, intf, metric) )
        {
            cerr<< "Error: failed to " << (enable ? "create" : "delete")
                << " route [" << address << '/' << masks << ' ' << intf.gateway << "]\n";
            continue;
        }

        ++success;
    }

    address_list.close();
    sock_cleanup();

    if( !quiet && !test_only )
    {
        cout<< "Done ! " << success << " routes " << (enable ? "added" : "deleted")
            << " successfully.\n";
    }
    return 0;
}
